<script setup lang="ts">
  import { ref, onMounted, useTemplateRef, watch, useSlots, computed } from 'vue'
  import type { QTableProps } from 'quasar'
  import { isRequestSuccess } from '@/utils'

  export type TQTableColumns = Required<QTableProps['columns']>
  export type TQTableRows = QTableProps['rows']

  export type TQTablePagination = QTableProps['pagination']
  export type TQTableRowsPerPageLabel = QTableProps['rowsPerPageLabel']
  export type TQTablePaginationLabel = QTableProps['paginationLabel']
  export type TQTableRowsPerPageOptions = QTableProps['rowsPerPageOptions']

  export type TQTableTitle = QTableProps['title']
  export type TQTableRowKey = QTableProps['rowKey']

  export type TQTableSelectedRowsLabel = QTableProps['selectedRowsLabel']
  export type TQTableSelection = QTableProps['selection']
  export type TQTableSelected = QTableProps['selected']
  export type TQTableOnRequest = Exclude<QTableProps['onRequest'], undefined>
  type TTableOmit =
    | 'columns'
    | 'pagination'
    | 'selected'
    | 'rowsPerPageLabel'
    | 'paginationLabel'
    | 'rowsPerPageOptions'
    | 'title'
    | 'rowKey'
    | 'selectedRowsLabel'
    | 'selection'
    | 'rows'
  export type TTableOptions = Omit<QTableProps, TTableOmit>

  export type TRequestProp = Parameters<TQTableOnRequest>[0]

  export type TRequestParam = {
    pagination: any
    table: any
    selected: TQTableSelected
  }

  export interface ICudTableProps {
    columns: TQTableColumns
    pagination?: TQTablePagination
    rowsPerPageLabel?: TQTableRowsPerPageLabel
    paginationLabel?: TQTablePaginationLabel
    rowsPerPageOptions?: TQTableRowsPerPageOptions
    title?: TQTableTitle
    rowKey?: TQTableRowKey
    selectedRowsLabel?: TQTableSelectedRowsLabel
    selection?: TQTableSelection
    options?: TTableOptions
    request?: (context: TRequestParam) => Promise<any>
    hidePagination?: boolean
  }

  // props
  const props = withDefaults(defineProps<ICudTableProps>(), {
    pagination: () => ({
      sortBy: 'desc',
      descending: false,
      page: 1,
      rowsPerPage: 10,
      rowsNumber: 10
    }),
    rowsPerPageLabel: '每页行数：',
    rowsPerPageOptions: () => [10, 20, 30, 40, 50, 100, 150],
    title: '',
    selection: 'none',
    selectedRowsLabel: (numberOfRows: number) => `已选择${numberOfRows}条`,
    options: () => ({}),
    rowKey: () => 'id',
    hidePagination: false
  })

  // data
  const tableRef = useTemplateRef('tableRef')
  const rowsRef = ref<TQTableRows>([])
  const selectedRef = ref<TQTableSelected>([])
  const paginationRef = ref<TQTablePagination>(props.pagination)
  const loadingRef = ref(false)
  const allRowsRef = ref(0)
  const pagesRef = ref(0)
  const curPageRef = ref(0)
  const rowsPerPageLabelRef = computed(
    () => `总行数：${allRowsRef.value}条   ${props.rowsPerPageLabel}`
  )
  if (props.hidePagination) {
    paginationRef.value!.rowsNumber = 100000
    paginationRef.value!.rowsPerPage = 100000
  }

  // emit
  const emit = defineEmits<{
    selected: [payload: any]
    refreshed: [payload: any]
  }>()
  interface IState {
    records: TQTableRows
    selected?: TQTableSelected
    current: number
    size: number
    pages: number
    total: number
  }
  function setTableState({ records, selected, current, size, pages, total }: IState) {
    rowsRef.value = records = records || []
    selectedRef.value = selected = selected || []
    paginationRef.value!.page = current || 1
    paginationRef.value!.rowsPerPage = size || 10
    paginationRef.value!.rowsNumber = total || 10
    allRowsRef.value = total
    curPageRef.value = current
    pagesRef.value = pages
    emit('refreshed', { tableRef, records, selected, paginationRef })
  }
  async function onRequest(rp: TRequestProp) {
    if (typeof props.request === 'function') {
      loadingRef.value = true
      if (paginationRef.value) {
        const res = await props.request({
          pagination: rp.pagination,
          table: tableRef.value,
          selected: selectedRef.value
        })
        if (isRequestSuccess(res)) {
          const { current, pages, records, size, total } = Array.isArray(res.data.data.records)
            ? res.data.data
            : { current: 1, pages: 1, size: 100000, total: 100000, records: res.data.data }
          setTableState({ current, pages, records, size, total })
        }
        loadingRef.value = false
      }
    }
  }

  watch(
    () => selectedRef.value,
    (nv, ov) => {
      emit('selected', { nv, ov })
    }
  )
  function refreshTable() {
    if (tableRef.value) {
      ;(tableRef.value as any).requestServerInteraction()
    }
  }
  function setPager(current: number, size: number) {
    paginationRef.value!.page = current || 1
    paginationRef.value!.rowsPerPage = size || 10
  }
  const slots = useSlots()
  function onKeyInput(event: KeyboardEvent) {
    if (event.key.toLowerCase() === 'enter') {
      // 13 为回车键
      onInputBlur()
    }
  }
  function onInputBlur() {
    const validPage =
      curPageRef.value > pagesRef.value
        ? pagesRef.value
        : curPageRef.value < 0
          ? 1
          : curPageRef.value
    paginationRef.value!.page = validPage
    refreshTable()
  }
  onMounted(() => {
    refreshTable()
  })
  function refreshUserTable(page: number = 1, size: number = 10): void {
    size = (size || paginationRef.value!.rowsPerPage) as number
    setPager(page, size)
    refreshTable()
  }
  defineExpose({
    refreshTable,
    refreshUserTable,
    tableRef,
    selectedRef,
    setTableState,
    setPager,
    paginationRef
  })
</script>

<template>
  <div class="cud-table-box" :class="{ 'hide-pagination': hidePagination }">
    <q-table
      ref="tableRef"
      :title="title"
      flat
      wrap-cells
      separator="cell"
      :rows-per-page-label="rowsPerPageLabelRef"
      :pagination-label="paginationLabel"
      :selected-rows-label="selectedRowsLabel"
      :rows-per-page-options="rowsPerPageOptions"
      :rows="rowsRef"
      :columns="columns"
      :row-key="rowKey"
      :selection="selection"
      v-model:pagination="paginationRef"
      v-model:selected="selectedRef"
      :loading="loadingRef"
      @request="onRequest"
      :hide-pagination="hidePagination"
      v-bind="options">
      <!-- 透传所有具名插槽 -->
      <template v-for="(_, name) in slots" #[name]="slotProps">
        <slot :name="name" v-bind="slotProps || {}"></slot>
      </template>
      <template v-slot:pagination="scope">
        <span class="mr-3">总页数：{{ pagesRef }}</span>
        <q-btn
          class="mx-2"
          padding="1px"
          icon="first_page"
          color="blue-6"
          text-color="white"
          dense
          :disable="scope.isFirstPage"
          @click="scope.firstPage"></q-btn>

        <q-btn
          class="mx-2"
          padding="1px"
          icon="chevron_left"
          color="blue-6"
          text-color="white"
          dense
          :disable="scope.isFirstPage"
          @click="scope.prevPage"></q-btn>
        <q-input
          outlined
          v-model="curPageRef"
          @blur="onInputBlur"
          @keydown="onKeyInput"
          class="cud-table-page-input" />
        <q-btn
          class="mx-2"
          padding="1px"
          icon="chevron_right"
          color="blue-6"
          text-color="white"
          dense
          :disable="scope.isLastPage"
          @click="scope.nextPage"></q-btn>

        <q-btn
          class="mx-2"
          icon="last_page"
          padding="1px"
          color="blue-6"
          text-color="white"
          dense
          :disable="scope.isLastPage"
          @click="scope.lastPage"></q-btn>
      </template>
    </q-table>
  </div>
</template>

<style scoped>
  .hide-pagination :deep(.q-table__middle) {
    margin-bottom: 20px;
    border-bottom: 1px solid rgba(0, 0, 0, 0.12);
  }
  .cud-table-box :deep(.q-table__container) {
    color: #333;
    padding: 0 20px;
    .q-table__top {
      padding: 12px 4px;
      .q-table__title {
        font-size: 20px;
        font-weight: 600;
      }
    }
    .q-table {
      border-left: 1px solid rgba(0, 0, 0, 0.12);
      border-right: 1px solid rgba(0, 0, 0, 0.12);
      th {
        font-size: 18px;
      }
      td {
        font-size: 16px;
      }
    }
  }
  .cud-table-box :deep(.q-table__bottom) {
    font-size: 18px;
    padding: 16px 0;

    .cud-table-page-input {
      width: 3rem;
    }

    .q-field--dense .q-field__marginal,
    .q-field__control {
      height: 30px !important;
    }

    .q-field--auto-height.q-field--dense .q-field__control {
      min-height: 28px !important;
    }

    .q-field--auto-height.q-field--dense .q-field__native {
      min-height: 30px;
    }

    .q-select--single {
      border: 1px solid rgba(0, 0, 0, 0.24);
      border-radius: 4px;
      padding: 0 10px;
    }
  }
  .mx-2.disabled {
    opacity: 0.4 !important;
  }
</style>
